package com.example.controller.pc;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.common.result.Result;
import com.example.common.result.YiFeiException;
import com.example.entity.dto.pc.PPRDto;
import com.example.entity.dto.pc.ScheduleDto;
import com.example.entity.dto.pc.aps.ApsGdDto;
import com.example.entity.excel.ApsEasyExcel;
import com.example.entity.hz.MOCTA;
import com.example.entity.hz.SFCTA;
import com.example.entity.mysql.Excelimportlog;
import com.example.entity.mysql.Productprocessresource;
import com.example.entity.mysql.Resourcegroup;
import com.example.entity.mysql.Workresource;
import com.example.entity.vo.pc.ApsGDList;
import com.example.entity.vo.pc.PCListVo;
import com.example.entity.yifei.Mocty;
import com.example.enums.MOCStatusEnum;
import com.example.exception.CustomException;
import com.example.mapper.MoctaMapper;
import com.example.mapper.pc.ScheduleMapper;
import com.example.service.ExcelimportlogService;
import com.example.service.MoctaService;
import com.example.service.MoctyService;
import com.example.service.SfctaService;
import com.example.service.pc.ProductprocessresourceService;
import com.example.service.pc.ResourcegroupService;
import com.example.service.pc.ScheduleService;
import com.example.service.pc.WorkresourceService;
import com.example.service.pc.aps.ApsService;
import com.example.untils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@RestController
@Api(tags = "APS")
@RequestMapping("/api/aps")
public class ApsController {

    @Resource
    private MoctaService moctaService;

    @Resource
    private ScheduleMapper scheduleMapper;

    @Resource
    private ScheduleService scheduleService;

    @Resource
    private ProductprocessresourceService productprocessresourceService;

    @Resource
    private ApsService apsService;
    @Autowired
    private MoctaMapper moctaMapper;
    @Resource
    private ResourcegroupService resourcegroupService;

    @Resource
    private SfctaService sfctaService;

    @Resource
    private MoctyService moctyService;

    @Autowired
    private WorkresourceService workresourceService;

    @Autowired
    private ExcelimportlogService excelimportlogService;

    @ApiOperation("apsMoctaList")
    @PostMapping("/apsMoctaList")
    public Result<List<ApsGDList>> apsMoctaList() {
        List<ApsGDList> list = moctaMapper.gdlb();
        return Result.success(list);
    }

    @ApiOperation("Excel导入自动排产")
    @PostMapping("/excelImportAps")
    @DSTransactional
    public Result<List<PCListVo>> excelImportAps(@RequestPart("file") MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        List<ApsEasyExcel> excelList = new ArrayList<>();
        EasyExcel.read(inputStream, ApsEasyExcel.class, new PageReadListener<ApsEasyExcel>(dataList -> {
            int idx = 2;
            for (ApsEasyExcel item : dataList) {
                item.setRows(idx);
                idx++;
                excelList.add(item);
            }
        })).sheet().doRead();
        Map<String, String> moctaProductIdMap = new HashMap<>();
        Map<String, String> sfctaProcessMap = new HashMap<>();
        Map<String, BigDecimal> moctaMap = new HashMap<>();  // 记录有多少不一样的工单
        for (ApsEasyExcel item : excelList) {
            if(StringUtils.isEmpty(item.getWorkSingle())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[工单单别]不能为空。\n");
            }
            if(StringUtils.isEmpty(item.getWorkNo())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[工单单号]不能为空。\n");
            }
            MOCTA one = moctaService.getOne(new LambdaQueryWrapper<MOCTA>().eq(MOCTA::getTA001, item.getWorkSingle()).eq(MOCTA::getTA002, item.getWorkNo()));
            if(one == null) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：工单不存在。\n");
            }
            if(!"Y".equals(one.getTA013().trim())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：工单未审核。\n");
            }
            if("Y".equals(one.getTA011().trim()) || "y".equals(one.getTA011().trim())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：工单已结束。\n");
            }
            moctaProductIdMap.put(one.getTA001().trim()+one.getTA002().trim(), one.getTA006().trim());
            if(StringUtils.isEmpty(item.getProcessNo())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[工序号]不能为空。\n");
            }
            if(!"****".equals(item.getProcessNo())) {
                SFCTA sfcta = sfctaService.getOne(new LambdaQueryWrapper<SFCTA>().eq(SFCTA::getTA001, item.getWorkSingle()).eq(SFCTA::getTA002, item.getWorkNo()).eq(SFCTA::getTA003, item.getProcessNo()));
                if(sfcta == null) {
                    throw new CustomException("500", "第" + item.getRows() + "行错误：工单没有此工序号。\n");
                }
                sfctaProcessMap.put(one.getTA001().trim()+one.getTA002().trim()+item.getProcessNo().trim(), sfcta.getTA004().trim());
            }
            if(StringUtils.isEmpty(item.getProductLine())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[生产线]不能为空。\n");
            }
            Resourcegroup resourcegroup = resourcegroupService.getOne(new LambdaQueryWrapper<Resourcegroup>().eq(Resourcegroup::getResourceGroupNO, item.getProductLine()));
            if(resourcegroup == null) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[生产线]编号不存在。\n");
            }
            if(StringUtils.isEmpty(item.getSum())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[预计产量]不能为空。\n");
            }
            if(!MoneyUtils.isNumber(item.getSum())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[预计产量]需要为正数。\n");
            }
            if(one.getTA015().compareTo(new BigDecimal(item.getSum())) < 0) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[预计产量]超过工单数量。\n");
            }
//            BigDecimal scheduledNum = scheduleMapper.scheduleNum(item.getWorkSingle(), item.getWorkNo(), item.getProcessNo());
//            if(scheduledNum.add(new BigDecimal(item.getSum())).compareTo(one.getTA015()) > 0) {
//                throw new CustomException("500", "第" + item.getRows() + "行错误：已排产数量: " + scheduledNum  + " + [预计产量]: " + item.getSum() + " 超过工单数量: " + one.getTA015() + "。\n");
//            }
            if(StringUtils.isEmpty(item.getOrderId())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[序号]不能为空。\n");
            }
            if(!MoneyUtils.isNumber(item.getOrderId())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[序号]需要为正数。\n");
            }
            if(StringUtils.isEmpty(item.getDailySum())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[日产量]不能为空。\n");
            }
            if(!MoneyUtils.isNumber(item.getDailySum())) {
                throw new CustomException("500", "第" + item.getRows() + "行错误：[日产量]需要为正数。\n");
            }
            String key = item.getWorkSingle().trim() + "@" + item.getWorkNo().trim() + "@" + item.getProcessNo().trim();
            BigDecimal bigDecimal = moctaMap.get(key);
            if(bigDecimal == null) bigDecimal = new BigDecimal(0D);
            bigDecimal = bigDecimal.add(new BigDecimal(item.getSum()));
            moctaMap.put(key, bigDecimal);
        }

        for (String key : moctaMap.keySet()) {
            BigDecimal zhi = moctaMap.get(key);
            String[] split = key.split("@");
            MOCTA one = moctaService.getOne(new LambdaQueryWrapper<MOCTA>().eq(MOCTA::getTA001, split[0]).eq(MOCTA::getTA002, split[1]));
            if(one == null) {
                throw new CustomException("500", "解析key有问题。key = " + key);
            }
            BigDecimal scheduledNum = scheduleMapper.scheduleNum(one.getTA001(), one.getTA002(), split[2]);
            if(scheduledNum.add(zhi).compareTo(one.getTA015()) > 0) {
                throw new CustomException("500", one.getTA001() + "-" + one.getTA002() + "：已排产数量: " + scheduledNum  + " + [预计产量]: " + zhi + " 超过工单数量: " + one.getTA015() + "。\n");
            }
        }

        Set<String> set = new HashSet<>();
        for (ApsEasyExcel item : excelList) {
            if(set.contains(item.getProductLine()+"-=-"+item.getOrderId())) {
                throw new CustomException("500", "生产线:" + item.getProductLine() + ", 序号:" + item.getOrderId() + " 重复。");
            }else{
                set.add(item.getProductLine()+"-=-"+item.getOrderId());
            }
        }

        // 按生产线进行分组
        Map<String, List<ApsEasyExcel>> map = new HashMap<>();
        for (ApsEasyExcel item : excelList) {
            List<ApsEasyExcel> list = map.get(item.getProductLine());
            if(list == null) list = new ArrayList<>();
            list.add(item);
            map.put(item.getProductLine(), list);
        }
        List<Mocty> moctyList = new ArrayList<>();
        // 进行插入操作
        for (Map.Entry<String, List<ApsEasyExcel>> entry : map.entrySet()) {
//            System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
            List<ApsEasyExcel> value = entry.getValue();
            value.sort(new Comparator<ApsEasyExcel>() {
                @Override
                public int compare(ApsEasyExcel o1, ApsEasyExcel o2) {
                    return o1.getRows().compareTo(o2.getRows());
                }
            });
            // 计算出最多需要用到的日期数，
            int need = 0;
            for(int i = 0; i < value.size(); i++) {
                need += (int) Math.ceil(Double.parseDouble(value.get(i).getSum()) / Double.parseDouble(value.get(i).getDailySum()));
            }
            Integer resourceGroupId = resourcegroupService.getOne(new LambdaQueryWrapper<Resourcegroup>().eq(Resourcegroup::getResourceGroupNO, entry.getKey())).getResourceGroupId();
            List<String> availableDateList = scheduleMapper.getAvailableDateFromMocty(TimeUtils.HZStdTime(), need, resourceGroupId.toString());
            // 计算出最多需要用到的日期数，  找到需要用到的日期，  将值统一插入日期中

            BigDecimal bl = new BigDecimal(0D);  // [0, 1)
            int idx = 0;
//            System.out.println("生产线: " + resourceGroupId);
            for(int i = 0; i < value.size(); i++) {    // 遍历所有的工单工序
                ApsEasyExcel excel = value.get(i);
                BigDecimal sum = new BigDecimal(excel.getSum());
                BigDecimal dailySum = new BigDecimal(excel.getDailySum());
                while(sum.compareTo(new BigDecimal(0D)) > 0) {
                    if(bl.compareTo(new BigDecimal(0D)) == 0) {      // 之前的日期没有被用完
                        Mocty mocty = new Mocty();
                        mocty.setCreateDate(TimeUtils.getNowTime());
                        mocty.setCreator(SecurityUtils.getLoginUser().getUsername());
                        mocty.setFlag(1D);
                        mocty.setTy001(excel.getWorkSingle());
                        mocty.setTy002(excel.getWorkNo());
                        mocty.setTy003(availableDateList.get(idx));
                        BigDecimal sl = null;
                        if(sum.compareTo(dailySum) < 0) sl = sum;
                        else sl = dailySum;
                        mocty.setTy004(sl.doubleValue());
                        mocty.setTy009(resourceGroupId.toString());
                        mocty.setTy019(excel.getProcessNo());
                        mocty.setUdf05(UUID.randomUUID().toString());
                        mocty.setUdf01(MOCStatusEnum.DaiJinZhan.getValue());
                        moctyList.add(mocty);
                        if(sl.compareTo(dailySum) != 0) bl = sum.divide(dailySum, 8, RoundingMode.HALF_UP);
                        sum = sum.subtract(sl);
//                        System.out.println("bl0 " + excel.getWorkSingle() + "-" + excel.getWorkNo() + "-" + availableDateList.get(idx) + "-" + sl);
                    }else{
                        Mocty mocty = new Mocty();
                        mocty.setCreateDate(TimeUtils.getNowTime());
                        mocty.setCreator(SecurityUtils.getLoginUser().getUsername());
                        mocty.setFlag(1D);
                        mocty.setTy001(excel.getWorkSingle());
                        mocty.setTy002(excel.getWorkNo());
                        mocty.setTy003(availableDateList.get(idx));
                        BigDecimal canMade = new BigDecimal(Math.floor(dailySum.multiply(new BigDecimal(1D).subtract(bl)).doubleValue()));
                        BigDecimal sl = null;
                        if(sum.compareTo(canMade) < 0) {
                            sl = sum;
                            bl = bl.add(sum.divide(dailySum, 8, RoundingMode.HALF_UP));
                        } else {
                            sl = canMade;
                            bl = new BigDecimal(0D);
                        }
                        mocty.setTy004(sl.doubleValue());
                        mocty.setTy009(resourceGroupId.toString());
                        mocty.setTy019(excel.getProcessNo());
                        mocty.setUdf05(UUID.randomUUID().toString());
                        mocty.setUdf01(MOCStatusEnum.DaiJinZhan.getValue());
                        moctyList.add(mocty);
                        sum = sum.subtract(sl);
//                        System.out.println("bl1 " + excel.getWorkSingle() + "-" + excel.getWorkNo() + "-" + availableDateList.get(idx) + "-" + sl);
                    }
                    if(bl.compareTo(new BigDecimal(0D)) == 0) idx++;
                }
            }
        }
        List<Workresource> workresourceList = new ArrayList<>();
        for(Mocty mocty : moctyList) {
            // 如果在品号工序资源表中有绑定默认的资源，直接写入表中。
            List<Productprocessresource> list = productprocessresourceService.selPPRList(new PPRDto(moctaProductIdMap.get(mocty.getTy001().trim()+mocty.getTy002().trim()),
                    sfctaProcessMap.get(mocty.getTy001().trim()+mocty.getTy002().trim()+mocty.getTy019().trim()), mocty.getTy019(), Integer.parseInt(mocty.getTy009())));
            Resourcegroup one = resourcegroupService.getOne(new LambdaQueryWrapper<Resourcegroup>().eq(Resourcegroup::getResourceGroupId, mocty.getTy009()));
            for(Productprocessresource t : list) {  // 插入品号工序资源组中默认配置的值
                if(t == null) continue;
                Workresource workresource = new Workresource();
                BeanUtils.copyPropertiesIgnoreCase(t, workresource);
                workresource.setWorkSingle(mocty.getTy001());
                workresource.setWorkNo(mocty.getTy002());
                workresource.setDate(mocty.getTy003());
                workresource.setCreateTime(TimeUtils.getFullNowTime());
                workresource.setWorkResourceId(UUID.randomUUID().toString());
                workresource.setProductLineId(mocty.getUdf05());   // 和生产线进行绑定
                // 如果已经指定了具体的生产线了，则更新那一条的具体的生产线/设备
                if(mocty.getTy009() != null && !"****".equals(mocty.getTy009().trim()) && t.getResourceGroupId().equals(one.getResourceGroupParentId())) {   // 如果有具体资源 && 具体资源和
                    workresource.setResourceId(Integer.parseInt(mocty.getTy009()));
                }
                workresourceList.add(workresource);
            }
        }
        moctyService.saveBatch(moctyList);
//        DynamicDataSourceContextHolder.push("mes");
        workresourceService.saveBatch(workresourceList);

        List<Excelimportlog> excelimportlogList = new ArrayList<>();
        for (ApsEasyExcel item : excelList) {
            Excelimportlog excelimportlog = new Excelimportlog();
            BeanUtils.copyPropertiesIgnoreCase(item, excelimportlog);
            excelimportlog.setCreator(SecurityUtils.getLoginUser().getUsername());
            excelimportlog.setCreateDate(TimeUtils.getFullNowTime());
            excelimportlogList.add(excelimportlog);
        }
        excelimportlogService.saveBatch(excelimportlogList);

//        ScheduleDto dto = new ScheduleDto();
//        List<String> moctaList = new ArrayList<>();
//        for(String item : moctaSet) {
//            moctaList.add(item);
//        }
//        dto.setMoctaList(moctaList);

//        DynamicDataSourceContextHolder.push("demo");
//        List<PCListVo> pcListVos = scheduleService.scheduleListExcelImport(dto);

// 单别单号是否存在，工序号是否存在, 工单是否已结束        序号是否有重复， 生产线设备是否存在，
//        Map<String, List<ApsEasyExcel>> map = new HashMap<>();
//        System.out.println(excelList);
        return Result.success();
    }

    //    @Log("工单自动排产")
    @ApiOperation("工单自动排产")
    @PostMapping("/apsMocta")
    @DSTransactional
    public Result apsMocta(@RequestBody List<ApsGdDto> gdList) {
        for(ApsGdDto gd : gdList) {
            // 拿到工单的所有工序
            ScheduleDto scheduleDto = new ScheduleDto();
            scheduleDto.setWorkSingle(gd.getWorkSingle());  scheduleDto.setWorkersNumber(gd.getWorkNo());
            List<PCListVo> list = scheduleMapper.scheduleListNoPage(scheduleDto);

            // 检查是否都是没有排产过的。
            for(PCListVo vo : list) {
//                qzSum.add(qzSum.get(qzSum.size() - 1) + Double.parseDouble(bommf.getUDF08()));
//                hzSum.add(bommf.getMF014().doubleValue());
                if(!vo.getSum().equals(vo.getScheduledNumber())) {
                    throw new YiFeiException(500, "工单:" + gd.getWorkSingle() + " , " + gd.getWorkNo() + "。已有工序排产过");
                }
            }
            // 筛掉节拍是零的 零的不用排产
            List<PCListVo> realList = new ArrayList<>();
            for(PCListVo vo : list) {
                // 获取工序的可用资源组
                List<Productprocessresource> resourceList = productprocessresourceService.list(new LambdaQueryWrapper<Productprocessresource>()
                        .eq(Productprocessresource::getProductId, vo.getProductId()).eq(Productprocessresource::getProcessNo, vo.getProcessNo())
                        .eq(Productprocessresource::getProcessId, vo.getProcessId()).and(i -> i.eq(Productprocessresource::getResourceTypeId, 1).or().eq(Productprocessresource::getResourceTypeId, 2)));
                // TODO 委外的工序需要关闭掉
                if(resourceList == null || resourceList.size() == 0) {
                    throw new YiFeiException(500, "工单:" + gd.getWorkSingle() + " , " + gd.getWorkNo() + "。工序: " + vo.getProcessNo() + " 没有绑定设备组或生产线组。");
                }
                vo.setResourceList(resourceList);
                Double zhi = null;
                Boolean flag = false;
                for(Productprocessresource t : resourceList) {
                    if(t.getPrimaryCode().equals(1)) {
                        zhi = t.getBeat();
                        flag = true;
                        break;
                    }
                }
                if(!flag) zhi = resourceList.get(0).getBeat();
                if(zhi == null) {
                    throw new YiFeiException(500, "工单:" + gd.getWorkSingle() + " , " + gd.getWorkNo() + "。工序: " + vo.getProcessNo() + " 没有绑定主要码节拍。");
                }
                if(!zhi.equals(0)) {
                    vo.setBeat(zhi);
                    realList.add(vo);
                }
            }
            apsService.apsMocta(realList);
        }
        return Result.success("成功");
    }

    // 查看资源组中有多少可用的资源，以及今天的负荷情况
//    private List<Productprocessresource> getAvailableResources(List<Productprocessresource> resourceList) {
//        List<Productprocessresource> res = new ArrayList<>();
//        for(Productprocessresource t : resourceList) {
//            if()
//        }
//        return res;
//    }

}
